﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;

namespace Pittypat
{
    /// <summary>
    /// 提供对字节数组的扩展方法。
    /// </summary>
    static class BinaryHelper
    {
        /// <summary>
        /// 一个长度为 0 的字节数组，这是只读静态字段。
        /// </summary>
        internal static readonly byte[] Empty = new byte[0];
        /// <summary>
        /// 在将 binary 与 bool 相互转换时表示 true 的数组，这是只读静态字段。
        /// </summary>
        internal static readonly byte[] True = new byte[] { 0x01 };

        /// <summary>
        /// 比较两个字节数组的大小。null 与空数组等价。
        /// </summary>
        /// <param name="x">第一个数组。null 与空数组等价。</param>
        /// <param name="y">第二个数组。null 与空数组等价。</param>
        /// <returns>如果 x 的长度大于 y，或者长度相等时，x 中的值大于 y 元素的值，则返回大于 0 的值。如果长度相同，所有元素值相同，则返回 0，否则返回小于 0 的值。</returns>
        internal static int Compare(byte[] x, byte[] y)
        {
            x = x ?? Empty;
            y = y ?? Empty;

            // 两者长度不同
            if (x.Length != y.Length)
            {
                return x.Length - y.Length;
            }

            // 两者长度相同
            int value = 0;
            for (int i = 0; i < x.Length; ++i)
            {
                value = x[i] - y[i];
                if (value != 0)
                {
                    return value;
                }
            }

            return 0;
        }

        /// <summary>
        /// 判断两个字节数组的值是否相等。null 与空数组等价。
        /// </summary>
        /// <param name="x">第一个数组。null 与空数组等价。</param>
        /// <param name="y">第二个数组。null 与空数组等价。</param>
        /// <returns>如果两个数组长度相同，并且元素的值相同，则返回 true，否则返回 false。</returns>
        internal static bool Equals(byte[] x, byte[] y)
        {
            x = x ?? Empty;
            y = y ?? Empty;

            // 两者长度不同
            if (x.Length != y.Length)
            {
                return false;
            }

            // 两者长度相同
            for (int i = 0; i < x.Length; ++i)
            {
                if (x[i] != y[i])
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 将指定数组的长度填充为目标长度，总是在最右边填充 0。
        /// </summary>
        /// <param name="v">目标数组，不改变该数组的值。</param>
        /// <param name="length">要填充的最终长度。</param>
        /// <returns>如果 v 的长度大于或者等于目标长度，则返回 v 本身，否则返回一个新的数组。</returns>
        internal static byte[] PadRight(byte[] v, int length)
        {
            v = v ?? Empty;

            if (v.Length >= length)
            {
                return v;
            }

            var d = new byte[length];

            // 复制前半部分
            Buffer.BlockCopy(v, 0, d, 0, v.Length);

            // 剩下的位置填充 0
            for (int i = v.Length; i < length; ++i)
            {
                d[i] = 0;
            }

            return d;
        }

        /// <summary>
        /// 判断指定的字节数组是否 null 或长度为 0。
        /// </summary>
        /// <param name="data">一个数组。</param>
        /// <returns>如果 data 为 null，或者长度为 0，则返回 true，否则返回 false。</returns>
        internal static bool IsNullOrEmpty(this byte[] data)
        {
            return data == null || data.Length == 0;
        }

        /// <summary>
        /// 从十六进制字符串解析字节数组。
        /// </summary>
        /// <param name="s">没有 "0x" 前缀的十六进制字符串。</param>
        /// <param name="startIndex">字符串中的起始位置。</param>
        /// <returns>解析后的字节数组，如果解析失败，返回 null。</returns>
        internal static byte[] FromHex(string s, int startIndex)
        {
            if (((s.Length - startIndex) % 2) != 0)
            {
                // 字符串的长度必须是偶数
                return null;
            }

            if (s.Length - startIndex == 0)
            {
                return Empty;
            }

            var bytes = new byte[(s.Length - startIndex) / 2];

            for (int i = startIndex, n = 0; i < s.Length; i += 2, ++n)
            {
                if (!byte.TryParse(s.Substring(i, 2), System.Globalization.NumberStyles.HexNumber, null, out bytes[n]))
                {
                    return null;
                }
            }

            return bytes;
        }

        /// <summary>
        /// 计算指定的字节数组的哈希代码。
        /// </summary>
        /// <param name="data">字节数组。</param>
        /// <returns>字节数组计算得到的哈希代码。</returns>
        internal static int ComputeHashCode(byte[] data)
        {
            if (data == null || data.Length == 0)
            {
                return 0;
            }

            unchecked
            {
                const int p = 16777619;
                int hash = (int)2166136261;

                for (int i = 0; i < data.Length; ++i)
                {
                    hash = (hash ^ data[i]) * p;
                }

                hash += hash << 13;
                hash ^= hash >> 7;
                hash += hash << 3;
                hash ^= hash >> 17;
                hash += hash << 5;

                return hash;
            }
        }
    }
}
